package globalVisionEntertainment.nba;

import gqqnbig.Tuple;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class NbaScorePredictor
{
	public static boolean verbose=false;
	
	private final Connection connection;

	private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

	public NbaScorePredictor(Connection databaseConnection)
	{
		this.connection = databaseConnection;
	}

	public String getGame(String homeTeam, String visitorTeam, String dateString) throws IllegalArgumentException
	{
		try
		{
			//@formatter:off
		PreparedStatement statement= connection.prepareStatement(
"SELECT id FROM nba.game \n"+
"where home=? and visitor=? and date=?");
		//@formatter:on

			statement.setString(1, homeTeam);
			statement.setString(2, visitorTeam);

			statement.setString(3, dateString);

			ResultSet resultSet = statement.executeQuery();
			if (resultSet.first() == false)
				throw new IllegalArgumentException(String.format("Unable to find the game %1s vs %2s on %3s", homeTeam, visitorTeam,
						dateString));

			String gameId = resultSet.getString(1);
			return gameId;
		}
		catch (SQLException e)
		{
			throw new RuntimeException(e);
		}
	}

	public PlayerPairScoreGain[] getPlayerPairScoreGains(String homeTeam, String visitorTeam, String dateString)
			throws UnsupportedOperationException
	{
		String gameId = getGame(homeTeam, visitorTeam, dateString);

		try
		{
			CallableStatement statement = connection.prepareCall("CALL `nba`.`selectMatchedPlayers`(?, ?)");
			statement.setString(1, gameId);
			statement.setBoolean(2, true);
			ResultSet resultSet = statement.executeQuery();

			resultSet.beforeFirst();

			String[] homeBenchs = getBenchPlayers(gameId, homeTeam);
			String[] visitorBenchs = getBenchPlayers(gameId, visitorTeam);

			String[] homeStarters = new String[5];
			String[] visitorStarters = new String[5];

			for (int i = 0; i < 5; i++)
			{
				if (resultSet.next() == false)
					throw new UnsupportedOperationException(String.format("Game %s doesn't have five starters.", gameId));

				homeStarters[i] = resultSet.getString(2);
				visitorStarters[i] = resultSet.getString(6);
				if (homeStarters[i] == null || visitorStarters[i] == null)
					throw new UnsupportedOperationException(String.format("Game %s doesn't have five starters.", gameId));
			}

			return getPlayerPairScoreGains(dateString, homeStarters, visitorStarters, homeBenchs, visitorBenchs);
		}
		catch (SQLException e)
		{
			throw new RuntimeException(e);
		}
	}

	public PlayerPairScoreGain[] getPlayerPairScoreGains(String dateString, String[] homeStarters, String[] visitorStarters,
			String[] homeBenchs, String[] visitorBenchs) throws SQLException
	{
		assert homeStarters.length == 5 && visitorStarters.length == 5;

		PlayerPairScoreGain[] scoreGains = new PlayerPairScoreGain[5 + gqqnbig.lang.Math.min(homeBenchs.length, visitorBenchs.length, 4)];

		for (int i = 0; i < homeStarters.length; i++)
		{
			scoreGains[i] = new PlayerPairScoreGain(homeStarters[i], visitorStarters[i]);
			scoreGains[i].homeGain.averageOnCourtMinutes = getAverageOnCourtMinutes(scoreGains[i].homeGain.playerName, dateString);
			scoreGains[i].visitorGain.averageOnCourtMinutes = getAverageOnCourtMinutes(scoreGains[i].visitorGain.playerName,
					dateString);
		}

		Tuple<String, Double>[] homeBenchPlayerTimes = getSortedPlayerAverageOnCourtTime(homeBenchs, dateString);
		Tuple<String, Double>[] visitorBenchPlayerTimes = getSortedPlayerAverageOnCourtTime(visitorBenchs, dateString);

		for (int i = 0; i < gqqnbig.lang.Math.min(homeBenchs.length, visitorBenchs.length, 4); i++)
		{
			PlayerPairScoreGain sg = new PlayerPairScoreGain(homeBenchPlayerTimes[i].item1, visitorBenchPlayerTimes[i].item1);
			scoreGains[i + 5] = sg;
			sg.homeGain.averageOnCourtMinutes = homeBenchPlayerTimes[i].item2;
			sg.visitorGain.averageOnCourtMinutes = visitorBenchPlayerTimes[i].item2;
		}

		return scoreGains;
	}

	private String[] getBenchPlayers(String gameId, String teamName)
	{

		try
		{
			//@formatter:off
			PreparedStatement statement= connection.prepareStatement(
"SELECT `PlayerName` FROM nba.playerstatistic\n"+
"where game=? and owner=? and isstarter=0;");
			//@formatter:on

			statement.setString(1, gameId);
			statement.setString(2, teamName);

			ResultSet resultSet = statement.executeQuery();

			resultSet.beforeFirst();

			ArrayList<String> list = new ArrayList<>();
			while (resultSet.next())
			{
				list.add(resultSet.getString(1));
			}

			String[] arr = new String[list.size()];
			list.toArray(arr);
			return arr;
		}
		catch (SQLException e)
		{
			throw new RuntimeException(e);
		}
	}

	public double getPridictedTotalScore(PlayerPairScoreGain[] scoreGains, String dateString)
	{
		double homeSum = 0;
		double visitorSum = 0;
		for (PlayerPairScoreGain scoreGain : scoreGains)
		{
			assert scoreGain != null : new UnsupportedOperationException();
			homeSum += scoreGain.homeGain.averageOnCourtMinutes;
			visitorSum += scoreGain.visitorGain.averageOnCourtMinutes;
		}

		for (int i = 0; i < scoreGains.length; i++)
		{
			PlayerPairScoreGain sg = scoreGains[i];
			// 5 players, 12 minutes per quarter, 4 quarters.
			sg.homeGain.proportionalOnCourtMinutes = sg.homeGain.averageOnCourtMinutes / homeSum * 5 * 12 * 4;
			sg.visitorGain.proportionalOnCourtMinutes = sg.visitorGain.averageOnCourtMinutes / visitorSum * 5 * 12 * 4;
		}

		for (PlayerPairScoreGain scoreGain : scoreGains)
			if (scoreGain.findScoreGain(connection, dateString) == false)
				return Double.NaN;

		double sum = 0;
		for (PlayerPairScoreGain scoreGain : scoreGains)
			sum += scoreGain.totalGain;

		assert scoreGains.length <= 10;
		return sum / 10;
	}

	private Tuple<String, Double>[] getSortedPlayerAverageOnCourtTime(String[] playerNames, String dateString) throws SQLException
	{

		Tuple<String, Double>[] playerTimes = new Tuple[playerNames.length];
		for (int i = 0; i < playerTimes.length; i++)
			playerTimes[i] = new Tuple<String, Double>(playerNames[i], getAverageOnCourtMinutes(playerNames[i], dateString));

		Arrays.sort(playerTimes, 0, playerTimes.length, new Comparator<Tuple<String, Double>>()
		{
			@Override
			public int compare(Tuple<String, Double> o1, Tuple<String, Double> o2)
			{
				if (o2.item2 > o1.item2)
					return 1;
				else if (o2.item2 < o1.item2)
					return -1;
				else
					return 0;
			}
		});

		return playerTimes;
	}

	private double getAverageOnCourtMinutes(String playerName, String dateString) throws SQLException
	{
		CallableStatement statement = connection.prepareCall("CALL `nba`.`getAverageOnCourtSeconds`(?, ?)");
		statement.setString(1, playerName);
		statement.setString(2, dateString);

		ResultSet resultSet = statement.executeQuery();
		resultSet.beforeFirst();
		double onCourtSeconds = 0;
		while (resultSet.next())
		{
			onCourtSeconds += resultSet.getDouble(2);
		}

		if (onCourtSeconds == 0)
			throw new UnsupportedOperationException(String.format("This is %s's first game.", playerName));
		return onCourtSeconds / 60;
	}

}